# LeetCode 367、有效的完全平方数
# 一、题目描述
给定一个 正整数 num
,编写一个函数,如果 num
是一个完全平方数,则返回 true
,否则返回 false
。
进阶:不要 使用任何内置的库函数,如 sqrt
。
示例 1:
输入:num = 16
输出:true
示例 2:
输入:num = 14
输出:false
提示:
1 <= num <= 2^31 - 1
# 二、题目解析
# 三、参考代码
# 1、Java 代码
// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 有效的完全平方数( LeetCode 367 ):https://leetcode-cn.com/problems/valid-perfect-square
class Solution {
public boolean isPerfectSquare(int num) {
// left 为当前区间最左侧的元素,可以获取到
int left = 0;
// right 为当前区间最右侧的元素,可以获取到
int right = num;
// 在区间 [left , right] 这个左闭右闭的区间里面去查找
// 查找什么呢?
// 查找是否存在一个数 a,使得 a * a = num
// 在 while 循环里面,left 不断的向右移动,而 right 不断的向左移动
// 当 [ left , right ] 这个区间不存在元素的时候,才跳出 while 循环,也就是当 left > right 时跳出循环
// 即当 left = right + 1 时,搜索区间没有元素了
// 由于 left 和 right 相遇的时候指向同一个元素,这个元素是存在于 [ left , right] 区间,这个区间就一个元素
// 所以此时 while 循环的判断可以使用等号
while (left <= right) {
// left + (right - left) / 2 和 (left + right) / 2 的结果相同
// 但是使用 left + (right - left) / 2 可以防止由于 left 、right 同时太大,导致相加的结果溢出的问题
// 比如数据 int 的最大值为 2,147,483,647
// 此时,left 为 2,147,483,640
// 此时,right 为 2,147,483,646
// 两者直接相加超过了 2,147,483,647,产生了溢出
int mid = left + (right - left) / 2;
// 判断中间元素的平方与 num 的大小
// 如果 mid * mid < num
// mid * mid 的数值有可能超过 int 类型的最大数
// 使用 long
long square = (long) mid * mid;
// 中间元素的平方小于了目标值 num
// 数组为 [ 0 , n ]
// 1、数组为有序数组,比如为递增数组
// 2、数组中不存在重复元素
// 由于该数组存在以上两个特点,所以 [ left , mid ] 这个区间中的所有元素的平方都小于目标值 num
// 因此,缩小查找区间为 [ mid + 1 , right]
if (square < num){
// 也就是说缩小之后的区间最左侧位置为 mid + 1
left = mid + 1;
// 中间元素的平方大于了目标值 num
// 数组为 [ 0 , n ]
// 1、数组为有序数组,比如为递增数组
// 2、数组中不存在重复元素
// 由于该数组存在以上两个特点,所以 [ mid , right ] 这个区间中的所有元素的平方都大于目标值 num
// 因此,缩小查找区间为 [ left , mid - 1]
}else if( square > num){
// 也就是说缩小之后的区间最右侧位置为 mid - 1
right = mid - 1 ;
// 中间元素的平方等于了目标值 num
}else if( square == num){
// 直接返回结果即可
return true;
}
}
// 查找完区间中的所有元素都没有找到,返回 false
return false;
}
}
# **2、**C++ 代码
// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 有效的完全平方数( LeetCode 367 ):https://leetcode-cn.com/problems/valid-perfect-square
class Solution {
public:
bool isPerfectSquare(int num) {
// left 为当前区间最左侧的元素,可以获取到
int left = 0;
// right 为当前区间最右侧的元素,可以获取到
int right = num;
// 在区间 [left , right] 这个左闭右闭的区间里面去查找
// 查找什么呢?
// 查找是否存在一个数 a,使得 a * a = num
// 在 while 循环里面,left 不断的向右移动,而 right 不断的向左移动
// 当 [ left , right ] 这个区间不存在元素的时候,才跳出 while 循环,也就是当 left > right 时跳出循环
// 即当 left = right + 1 时,搜索区间没有元素了
// 由于 left 和 right 相遇的时候指向同一个元素,这个元素是存在于 [ left , right] 区间,这个区间就一个元素
// 所以此时 while 循环的判断可以使用等号
while (left <= right) {
// left + (right - left) / 2 和 (left + right) / 2 的结果相同
// 但是使用 left + (right - left) / 2 可以防止由于 left 、right 同时太大,导致相加的结果溢出的问题
// 比如数据 int 的最大值为 2,147,483,647
// 此时,left 为 2,147,483,640
// 此时,right 为 2,147,483,646
// 两者直接相加超过了 2,147,483,647,产生了溢出
int mid = left + (right - left) / 2;
// 判断中间元素的平方与 num 的大小
// 如果 mid * mid < num
// mid * mid 的数值有可能超过 int 类型的最大数
// 使用 long
long square = (long) mid * mid;
// 中间元素的平方小于了目标值 num
// 数组为 [ 0 , n ]
// 1、数组为有序数组,比如为递增数组
// 2、数组中不存在重复元素
// 由于该数组存在以上两个特点,所以 [ left , mid ] 这个区间中的所有元素的平方都小于目标值 num
// 因此,缩小查找区间为 [ mid + 1 , right]
if (square < num){
// 也就是说缩小之后的区间最左侧位置为 mid + 1
left = mid + 1;
// 中间元素的平方大于了目标值 num
// 数组为 [ 0 , n ]
// 1、数组为有序数组,比如为递增数组
// 2、数组中不存在重复元素
// 由于该数组存在以上两个特点,所以 [ mid , right ] 这个区间中的所有元素的平方都大于目标值 num
// 因此,缩小查找区间为 [ left , mid - 1]
}else if( square > num){
// 也就是说缩小之后的区间最右侧位置为 mid - 1
right = mid - 1 ;
// 中间元素的平方等于了目标值 num
}else if( square == num){
// 直接返回结果即可
return true;
}
}
// 查找完区间中的所有元素都没有找到,返回 false
return false;
}
};
# 3、Python 代码
登录 AlgoMooc 官网获取更多算法图解
# https://www.algomooc.com
# 作者:程序员吴师兄
# 代码有看不懂的地方一定要私聊咨询吴师兄呀
# 有效的完全平方数( LeetCode 367 ):https://leetcode-cn.com/problems/valid-perfect-square
class Solution:
def isPerfectSquare(self, num: int) -> bool:
# left 为当前区间最左侧的元素,可以获取到
left = 0
# right 为当前区间最右侧的元素,可以获取到
right = num
# 在区间 [left , right] 这个左闭右闭的区间里面去查找
# 查找什么呢?
# 查找是否存在一个数 a,使得 a * a = num
# 在 while 循环里面,left 不断的向右移动,而 right 不断的向左移动
# 当 [ left , right ] 这个区间不存在元素的时候,才跳出 while 循环,也就是当 left > right 时跳出循环
# 即当 left = right + 1 时,搜索区间没有元素了
# 由于 left 和 right 相遇的时候指向同一个元素,这个元素是存在于 [ left , right] 区间,这个区间就一个元素
# 所以此时 while 循环的判断可以使用等号
while left <= right :
# left + (right - left) / 2 和 (left + right) / 2 的结果相同
# 但是使用 left + (right - left) / 2 可以防止由于 left 、right 同时太大,导致相加的结果溢出的问题
# 比如数据 的最大值为 2,147,483,647
# 此时,left 为 2,147,483,640
# 此时,right 为 2,147,483,646
# 两者直接相加超过了 2,147,483,647,产生了溢出
mid = left + (right - left) // 2
# 判断中间元素的平方与 num 的大小
# 如果 mid * mid < num
# mid * mid 的数值有可能超过 类型的最大数
# 使用 long
square = mid * mid
# 中间元素的平方小于了目标值 num
# 数组为 [ 0 , n ]
# 1、数组为有序数组,比如为递增数组
# 2、数组中不存在重复元素
# 由于该数组存在以上两个特点,所以 [ left , mid ] 这个区间中的所有元素的平方都小于目标值 num
# 因此,缩小查找区间为 [ mid + 1 , right]
if square < num:
# 也就是说缩小之后的区间最左侧位置为 mid + 1
left = mid + 1
# 中间元素的平方大于了目标值 num
# 数组为 [ 0 , n ]
# 1、数组为有序数组,比如为递增数组
# 2、数组中不存在重复元素
# 由于该数组存在以上两个特点,所以 [ mid , right ] 这个区间中的所有元素的平方都大于目标值 num
# 因此,缩小查找区间为 [ left , mid - 1]
elif square > num :
# 也就是说缩小之后的区间最右侧位置为 mid - 1
right = mid - 1
# 中间元素的平方等于了目标值 num
elif square == num :
# 直接返回结果即可
return True
# 查找完区间中的所有元素都没有找到,返回 False
return False